package com.zhu.tool_test.starke.util.weapons;

import lombok.Data;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;

/**
 * TransactionWeapons
 * supply transactional method invoker
 *
 * @author : 4everlynn, Ztrue
 * @date : 2019-07-23
 * R means result of target method, F means final result
 */
public class TransactionWeapons<R, F> {

    /**
     * run result callback
     */
    private Callable<R> resultCallBack;

    /**
     * success callback
     */
    private StarkeCallable<F, R> successCallBack;

    /**
     * error callback
     */
    private StarkeCallable<F, Exception> errorCallBack;

    /**
     * collect current callback to guarantee safety
     */
    @Data
    private class TransactionInvoker {
        /**
         * run result callback
         */
        private Callable<R> resultCallBack;

        /**
         * success callback
         */
        private StarkeCallable<F, R> successCallBack;

        /**
         * error callback
         */
        private StarkeCallable<F, Exception> errorCallBack;
    }

    /**
     * run transactional method synchronize
     *
     * @return final result
     */
    @Transactional(rollbackFor = Exception.class)
    public F sync() {
        TransactionInvoker transactionCollector = new TransactionInvoker();
        transactionCollector.setSuccessCallBack(successCallBack);
        transactionCollector.setErrorCallBack(errorCallBack);
        transactionCollector.setResultCallBack(resultCallBack);
        try {
            R res = transactionCollector.getResultCallBack().call();
            return transactionCollector.getSuccessCallBack().call(res);
        } catch (Exception e) {
            e.printStackTrace();
            try {
                return transactionCollector.getErrorCallBack().call(e);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    /**
     * run method async
     * return feature could change async to sync
     *
     * @return feature
     */
    @Transactional(rollbackFor = Exception.class)
    public CompletableFuture<R> async() {
        TransactionInvoker transactionCollector = new TransactionInvoker();
        transactionCollector.setSuccessCallBack(successCallBack);
        transactionCollector.setErrorCallBack(errorCallBack);
        transactionCollector.setResultCallBack(resultCallBack);
        return CompletableFuture.supplyAsync(() -> {
            try {
                return transactionCollector.getResultCallBack().call();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }).whenComplete((resultCallBack, e) -> {
            try {
                if (null != transactionCollector.getSuccessCallBack()) {
                    transactionCollector.getSuccessCallBack().call(resultCallBack);
                }
             } catch (Exception ex) {
                try {
                    if (null != transactionCollector.getErrorCallBack()) {
                        transactionCollector.getErrorCallBack().call(ex);
                    }
                } catch (Exception ext) {
                    ext.printStackTrace();
                }
                ex.printStackTrace();
            }
        });
    }


    public TransactionWeapons<R, F> error(StarkeCallable<F, Exception> errorCallBack) {
        this.errorCallBack = errorCallBack;
        return this;
    }

    public TransactionWeapons<R, F> success(StarkeCallable<F, R> successCallBack) {
        this.successCallBack = successCallBack;
        return this;
    }

    public TransactionWeapons<R, F> run(Callable<R> runCallBack) {
        this.resultCallBack = runCallBack;
        return this;
    }
}
